package com.juhuixing.common.utils.DFA;

import com.google.common.collect.Lists;
import com.juhuixing.common.enums.BaseEnum;
import com.juhuixing.common.exception.BusinessException;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Set;

/**
 * @ClassName DFA
 * @Description TODO
 * @Author 刘猛
 * @Date 2019/6/28 10:45
 **/
public class DFA {

    /**
     * 根节点
     */
    private static TreeNode rootNode = new TreeNode();

    /**
     * 创建关键词树时的临时根节点
     */
    private static TreeNode rootNodeTemp = new TreeNode();

    /**
     * 关键词缓存
     */
    private static ByteBuffer keywordBuffer = ByteBuffer.allocate(1024);

    /**
     * 关键词编码
     */
    private static String charset = "utf-8";

    /**
     * 关键词树是否初始化
     */
    private static boolean treeIsInit = false;

    /**
     * 创建DFA树
     * @param keywords
     * @throws UnsupportedEncodingException
     */
    @SneakyThrows
    public static <T> void createKeywordTree(T keywords){
        List<String> keywordList = Lists.newArrayList();
        if(keywords instanceof List){
            keywordList = (List<String>)keywords;
        }else if(keywords instanceof Set){
            Set<String> sets = (Set<String>)keywords;
            keywordList = Lists.newArrayList(sets);
        }

        for (String keyword : keywordList) {
            if(keyword == null) {continue;}
            keyword = keyword.trim().toLowerCase();
            byte[] bytes = keyword.getBytes(charset);
            TreeNode tempNode = rootNodeTemp;
            for (int i = 0; i < bytes.length; i++) {
                int index = bytes[i] & 0xff;
                TreeNode node = tempNode.getSubNode(index);
                if(node == null){
                    node = new TreeNode();
                    tempNode.setSubNode(index, node);
                }
                tempNode = node;
                if(i == bytes.length - 1){
                    tempNode.setKeywordEnd(true);
                }
            }
        }
        synchronized (DFA.class)
        {
            rootNode = rootNodeTemp;
            if(!treeIsInit) {
                treeIsInit = true;
            }
        }

    }


    /**
     * 检索关键词
     * @param text
     * @return
     * @throws UnsupportedEncodingException
     */
    @SneakyThrows
    public static CheckResult searchKeyword(String text){
        if(StringUtils.isEmpty(text)){
            return CheckResult.builder().isLegal(BaseEnum.YES.getCode()).handleResult("").build();
        }
        if(!treeIsInit) {
            throw new BusinessException("tree is not init!");
        }
        return searchKeyword(text.getBytes(charset),text.toLowerCase().getBytes(charset));
    }

    /**
     * 检索关键词
     * @param oldBytes
     * @param bytes
     * @return
     */
    @SneakyThrows
    public static CheckResult searchKeyword(byte[] oldBytes,byte[] bytes){
        Integer is_legal = BaseEnum.YES.getCode();
        String result;
        if(bytes == null || bytes.length == 0){
            result = "";
        }else{
            synchronized (DFA.class)
            {
                TreeNode tempNode = rootNode;
                int rollback = 0;
                int position = 0;
                while (position < bytes.length) {
                    int index = bytes[position] & 0xFF;
                    keywordBuffer.put(bytes[position]);
                    tempNode = tempNode.getSubNode(index);
                    if(tempNode == null){
                        position = position - rollback;
                        rollback = 0;
                        tempNode = rootNode;
                        keywordBuffer.clear();
                    }
                    else if(tempNode.isKeywordEnd()){
                        keywordBuffer.flip();
                        for (int i = 0; i <= rollback; i++) {
                            bytes[position-i] = 42;
                            oldBytes[position-i] = 42;
                        }
                        keywordBuffer.limit(keywordBuffer.capacity());
                        rollback = 1;
                        is_legal = BaseEnum.NO.getCode();
                    }else{
                        rollback++;
                    }
                    position++;
                }
            }
            result  =  new String(oldBytes,charset);
            result = result.replaceAll("\\*\\*\\*","*");
        }
        return CheckResult.builder().isLegal(is_legal).handleResult(result).build();
    }

    public void setCharset(String charset) {
        DFA.charset = charset;
    }

}
